﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace WCommon.Sockets
{
    /// <summary>
    /// Socket通用类
    /// </summary>
    public class SocketCommon
    {
        private static int port = 5000;
        private static string host = "127.0.0.1";
        private static IPAddress ip = IPAddress.Parse(host);
        private static IPEndPoint ipe = new IPEndPoint(ip, port);

        #region 同步Socket

        /// <summary>
        /// 同步方式启动Socket服务端
        /// </summary>
        public static void SyncServerStart()
        {
            using (Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
            {
                socket.Bind(ipe);
                socket.Listen(5000);
                Console.WriteLine(string.Format("{0}:{1}监听已经打开，请等待。。。", host, port));

                Socket serverSocket = socket.Accept();
                while (true)
                {
                    string recStr = "";
                    byte[] recByte = new byte[4096];
                    int bytes = serverSocket.Receive(recByte, recByte.Length, 0);
                    recStr += Encoding.ASCII.GetString(recByte, 0, bytes);

                    Console.WriteLine("服务器端获得消息：{0}", recStr);
                    string sendStr = "client send: " + recStr;
                    byte[] sendByte = Encoding.ASCII.GetBytes(sendStr);
                    serverSocket.Send(sendByte, sendByte.Length, 0);
                }
            }
        }

        /// <summary>
        /// 同步方式启动Socket客户端
        /// </summary>
        public static void SyncClientStart()
        {
            using (Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
            {
                clientSocket.Connect(ipe);

                while (true)
                {
                    string sendStr = Console.ReadLine(); // "send to server :hello server";
                    byte[] sendBytes = Encoding.ASCII.GetBytes(sendStr);
                    clientSocket.Send(sendBytes);

                    string recStr = "";
                    byte[] recBytes = new byte[4096];
                    int bytes = clientSocket.Receive(recBytes, recBytes.Length, SocketFlags.None);
                    recStr += Encoding.ASCII.GetString(recBytes, 0, bytes);
                    Console.WriteLine("从服务器接收到：{0}", recStr);
                }
            }
        }

        #endregion

        #region 异步Socket

        private static Socket _asyncServerSocket;
        private static Socket _asyncClientSocket;
        private static SocketAsyncEventArgs _serverArgs;

        #region 服务端

        /// <summary>
        /// 异步方式启动Socket服务端
        /// </summary>
        public static void AsyncServerStart()
        {
            _asyncServerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            _asyncServerSocket.Bind(ipe);
            _asyncServerSocket.Listen(5000);
            Console.WriteLine(string.Format("{0}:{1}监听已经打开，请等待。。。", host, port));

            _serverArgs = new SocketAsyncEventArgs();
            _serverArgs.Completed += AcceptSocketArgs_Completed;

            StartAccepting();
        }

        /// <summary>
        /// 开始接收
        /// </summary>
        private static void StartAccepting()
        {
            var fireAsync = _asyncServerSocket.AcceptAsync(_serverArgs);
            if (!fireAsync)
                ProcessAccept(_serverArgs);
            //catch (Exception)
            //{
            //    Task.Factory.StartNew(() => StartAccepting());
            //}
        }

        /// <summary>
        /// 异步Socket事件完成后事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private static void AcceptSocketArgs_Completed(object sender, SocketAsyncEventArgs e)
        {
            ProcessAccept(e);   
        }

        /// <summary>
        /// 接收处理
        /// </summary>
        /// <param name="e"></param>
        private static void ProcessAccept(SocketAsyncEventArgs e)
        {
            if(e.SocketError == SocketError.Success)
            {
                var acceptSocket = e.AcceptSocket;
                e.AcceptSocket = null;
                OnSocketAccepted(acceptSocket, e.UserToken);
            }
            else
            {
                e.AcceptSocket.Shutdown(SocketShutdown.Both);
                e.AcceptSocket = null;
            }

            StartAccepting();
        }

        private static void OnSocketAccepted(Socket socket, object userToken)
        {
            Task.Factory.StartNew(() =>
            {
                SocketAsyncEventArgs receiveAsync = new SocketAsyncEventArgs();
                receiveAsync.Completed += ReceiveAsync_Completed;

                if (!socket.ReceiveAsync(receiveAsync))
                    OnReceive(socket, receiveAsync);
            });
        }

        private static void ReceiveAsync_Completed(object sender, SocketAsyncEventArgs e)
        {
            OnReceive(sender, e);
        }

        private static void OnReceive(object sender, SocketAsyncEventArgs e)
        {
            if (e.SocketError == SocketError.Success && e.BytesTransferred > 0)
            {
                byte[] recByte = new byte[4096];
                Array.Copy(e.Buffer, e.Offset, recByte, 0, e.Buffer.Length);
                string message = Encoding.Unicode.GetString(recByte);
                Console.WriteLine("接收到数据：{0}", message);
            }
        }

        #endregion

        #region 客户端


        /// <summary>
        /// 异步方式启动Socket客户端
        /// </summary>
        public static void AsyncClientStart()
        {
            _asyncClientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            var socketArgs = new SocketAsyncEventArgs
            {
                AcceptSocket = _asyncClientSocket,
                RemoteEndPoint = ipe,
            };
            socketArgs.Completed += SocketArgs_Completed;

            var firedAsync = _asyncClientSocket.ConnectAsync(socketArgs);
            if (!firedAsync)
                ProcessConnect(socketArgs);

            new ManualResetEvent(false).WaitOne(5000);
        }

        private static void SocketArgs_Completed(object sender, SocketAsyncEventArgs e)
        {
            ProcessConnect(e);
        }

        private static void ProcessConnect(SocketAsyncEventArgs e)
        {
            e.Completed -= SocketArgs_Completed;
            e.RemoteEndPoint = null;
            e.AcceptSocket = null;
            e.Dispose();

            if(e.SocketError == SocketError.Success)
            {
                SocketAsyncEventArgs sendAsync = new SocketAsyncEventArgs();
                sendAsync.Completed += SendAsync_Completed;
                TrySend(sendAsync);
            }
            else
            {
                _asyncClientSocket.Shutdown(SocketShutdown.Both);
                _asyncClientSocket.Close(1000);
            }
        }

        private static void TrySend(SocketAsyncEventArgs sendAsync)
        {
            var sendBuffer = Encoding.Unicode.GetBytes("hello ");
            sendAsync.SetBuffer(sendBuffer, 0, sendBuffer.Length);
            if (!_asyncClientSocket.SendAsync(sendAsync))
                Task.Factory.StartNew(() => OnSend(sendAsync));
        }

        private static void SendAsync_Completed(object sender, SocketAsyncEventArgs e)
        {
            OnSend(e);
        }

        private static void OnSend(SocketAsyncEventArgs e)
        {
            if(e.SocketError == SocketError.Success)
            {
                TrySend(e);
            }
            else
            {
                _asyncClientSocket.Close();
            }
        }

        #endregion

        #endregion

        public static void StartServer2()
        {
            Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            socket.Bind(ipe);
            socket.Listen(5000);
            Console.WriteLine(string.Format("{0}:{1}监听已经打开，请等待。。。", host, port));

            socket.BeginAccept(new AsyncCallback(ClientAccepted), socket);
        }

        static byte[] buffer = new byte[1024];
        private static void ClientAccepted(IAsyncResult ar)
        {
            var socket = ar.AsyncState as Socket;
            var client = socket.EndAccept(ar);

            string clientIp = ((System.Net.IPEndPoint)client.RemoteEndPoint).Address.ToString();
            string clientPort = ((System.Net.IPEndPoint)client.RemoteEndPoint).Port.ToString();
            client.Send(Encoding.Unicode.GetBytes("Hi " + clientIp + ":" + clientPort + ", I accept you request at " + DateTime.Now.ToString()));

            client.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveMessage), client);

            socket.BeginAccept(new AsyncCallback(ClientAccepted), socket);
        }

        private static void ReceiveMessage(IAsyncResult ar)
        {
            var socket = ar.AsyncState as Socket;
            string clientIp = ((IPEndPoint)socket.RemoteEndPoint).Address.ToString();
            string clientPort = ((IPEndPoint)socket.RemoteEndPoint).Port.ToString();
            var length = socket.EndReceive(ar);
            var message = Encoding.Unicode.GetString(buffer, 0, length);
            Console.WriteLine("接收到：{0}", message);
            socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveMessage), socket);
        }

        public static void StartClient2()
        {
            Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            clientSocket.Connect(ipe);
            clientSocket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveMessage), clientSocket);

            Helper.WhileTrue(() =>
            {
                var outputBuffer = Encoding.Unicode.GetBytes(Console.ReadLine());
                clientSocket.BeginSend(outputBuffer, 0, outputBuffer.Length, SocketFlags.None, null, null);
            });
        }
    }
}
